Spring框架提供为应用透明添加缓存的支持,核心思想是,将抽象应用到缓存方法,基于缓存中可用信息减少方法的执行。缓存逻辑的应用是透明的,不会干扰调用者。
注 具体参考Spring框架指南的相应章节。
简而言之,为服务的某个操作添加缓存跟为方法添加相应注解那样简单:
import javax.cache.annotation.CacheResult;
import org.springframework.stereotype.Component;
@Component
public class MathService {
@CacheResult
public int computePiDecimal(int i) {
// ...
}
}
注 你既可以使用标准的JSR-107 (JCache)注解,也可以使用Spring自己的缓存注解,这是透明的,我们强烈建议你不要混淆使用。
缓存抽象不提供实际的存储,而是依赖于org.springframework.cache.Cache
和org.springframework.cache.CacheManager
接口的实现。只要通过@EnableCaching
注解开启缓存支持,Spring Boot就会根据实现自动配置一个合适的CacheManager
。
注 如果你使用的缓存设施beans不是基于接口的,确保启用proxyTargetClass
,并设置其属性为@EnableCaching
。
注 使用spring-boot-starter-cache
‘Starter’可以快速添加所需缓存依赖,如果你是手动添加依赖,需要注意一些实现只有spring-context-support
jar才提供。
如果你还没有定义一个CacheManager
类型的bean,或一个名为cacheResolver
的CacheResolver
(查看CachingConfigurer
),Spring Boot将尝试以下提供商(按这个顺序):
注 spring.cache.type
属性可强制指定使用的缓存提供商,如果需要在一些环境(比如,测试)中禁用全部缓存也可以使用该属性。
如果CacheManager
是Spring Boot自动配置的,你可以在它完全初始化前,通过实现CacheManagerCustomizer
接口进一步配置,以下设置使用的缓存name:
java
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(Arrays.asList("one", "two"));
}
};
}
注 在以上示例中,需要配置一个ConcurrentMapCacheManager
,如果没有配置,则自定义器(customizer)将不会被调用。自定义器你添加多少都可以,并可以使用@Order
或Ordered
对它们进行排序。
如果上下文定义至少一个org.springframework.cache.Cache
bean,一个配置好的CacheManager
包装着它们,那么将使用通用(Generic)缓存。
classpath下存在javax.cache.spi.CachingProvider
(比如,一个遵循JSR-107的缓存library),则JCache将启动。这里有很多遵循JSR-107的libraries,Spring Boot为Ehcache 3, Hazelcast和Infinispan提供依赖管理,其他library也可以像这样添加。
如果出现多个提供商,你需要明确指定使用哪个(提供商)。尽管JSR-107标准没有强制定义配置文件的位置,Spring Boot会尽量配合各实现情况:
# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.acme.MyCachingProvider
spring.cache.jcache.config=classpath:acme.xml
注 由于一个缓存library可能提供的既有native实现,也有JSR-107支持,Spring Boot将优先使用JSR-107支持,这样如果你切换到不同的JSR-107实现,相同特性依旧可以使用。
以下方式可以自定义底层的javax.cache.cacheManager
:
通过设置spring.cache.cache-names
属性,缓存可以在启动时就被创建。如果定义一个javax.cache.configuration.Configuration
bean,它将用于自定义缓存。
使用CacheManager
的引用调用org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer
beans可完成全部配置。
注 如果定义标准的javax.cache.CacheManager
,它将自动包装进org.springframework.cache.CacheManager
以实现预期的抽象,也不能对它进一步配置了。
如果在classpath下的根目录可以找到一个名为ehcache.xml
的文件,则缓存将使用EhCache 2.x。如果EhCache 2.x和这样的文件出现,那它们将用于启动缓存管理器,使用以下配置可提供替换的配置文件:
spring.cache.ehcache.config=classpath:config/another-config.xml
Spring Boot为Hazelcast提供通常的支持,如果HazelcastInstance
被自动配置,那它将自动包装进一个CacheManager
。
如果出于某些原因,需要使用另一个不同的HazelcastInstance
,你可以请求Spring Boot创建一个单独的实例,并只用于该CacheManager
:
spring.cache.hazelcast.config=classpath:config/my-cache-hazelcast.xml
注 如果以这种方式创建一个单独的HazelcastInstance
,它将不会注册到应用上下文中。
Infinispan没有默认的配置文件位置,所以需要显式指定:
spring.cache.infinispan.config=infinispan.xml
通过设置spring.cache.cache-names
属性可以让缓存在启动时就被创建,如果定义了ConfigurationBuilder
bean,它将用来定义该实例。
如果Couchbase可用,并配置好了,CouchbaseCacheManager
将会自动配置,使用spring.cache.cache-names
属性可以在启动时创建其他缓存。对Bucket
的操作也是自动配置的,你可以使用customizer在另一个Bucket
上创建其他缓存:假设你需要在“main” Bucket
上存放两个缓存(foo
和bar
),在另一个Bucket
上存放一个存活时间为2秒的biz
缓存。首先,你通过配置创建两个缓存:
spring.cache.cache-names=foo,bar
然后定义其他@Configuration
来配置另一个Bucket
和biz
缓存:
@Configuration
public class CouchbaseCacheConfiguration {
private final Cluster cluster;
public CouchbaseCacheConfiguration(Cluster cluster) {
this.cluster = cluster;
}
@Bean
public Bucket anotherBucket() {
return this.cluster.openBucket("another", "secret");
}
@Bean
public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
return c -> {
c.prepareCache("biz", CacheBuilder.newInstance(anotherBucket())
.withExpirationInMillis(2000));
};
}
}
这个示例配置重用了通过自动配置的Cluster
。
如果Redis可用,并配置好了,RedisCacheManager
将被自动配置,使用spring.cache.cache-names
可以在启动时创建其他缓存。
注 默认会添加key前缀以防止两个单独的缓存使用相同的key,否则Redis将存在重复的key,有可能返回不可用的值。如果创建自己的RedisCacheManager
,强烈建议你保留该配置处于启用状态。
Caffeine是Java8对Guava缓存的重写版本,在Spring Boot 2.0中将取代Guava。如果出现Caffeine,CaffeineCacheManager
将会自动配置。使用spring.cache.cache-names
属性可以在启动时创建缓存,并可以通过以下配置进行自定义(按顺序):
spring.cache.caffeine.spec
定义的特殊缓存com.github.benmanes.caffeine.cache.CaffeineSpec
bean定义com.github.benmanes.caffeine.cache.Caffeine
bean定义例如,以下配置创建一个foo
和bar
缓存,最大数量为500,存活时间为10分钟:
spring.cache.cache-names=foo,bar
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
除此之外,如果定义了com.github.benmanes.caffeine.cache.CacheLoader
,它会自动关联到CaffeineCacheManager
。由于该CacheLoader
将关联被该缓存管理器管理的所有缓存,所以它必须定义为CacheLoader<Object, Object>
,自动配置将忽略所有泛型类型。
如果存在Guava,GuavaCacheManager
会自动配置。使用spring.cache.cache-names
属性可以在启动时创建缓存,并通过以下方式之一自定义(按此顺序):
spring.cache.guava.spec
定义的特殊缓存com.google.common.cache.CacheBuilderSpec
bean定义的com.google.common.cache.CacheBuilder
bean定义的例如,以下配置创建了一个foo
和bar
缓存,该缓存最大数量为500,存活时间为10分钟:
spring.cache.cache-names=foo,bar
spring.cache.guava.spec=maximumSize=500,expireAfterAccess=600s
此外,如果定义com.google.common.cache.CacheLoader
bean,它会自动关联到GuavaCacheManager
。由于该CacheLoader
将关联该缓存管理器管理的所有缓存,它必须定义为CacheLoader<Object, Object>
,自动配置会忽略所有泛型类型。
如果以上选项都没有采用,一个使用ConcurrentHashMap
作为缓存存储的简单实现将被配置,这是应用没有添加缓存library的默认设置。
如果配置类中出现@EnableCaching
,一个合适的缓存配置也同样被期待。如果在某些环境需要禁用全部缓存,强制将缓存类型设为none
将会使用一个no-op实现(没有任何实现的实现):
spring.cache.type=none